﻿using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System;
using System.Collections.Generic;
using DimensionsHelper.Common.Contracts.Metadata;
// ReSharper disable RedundantSuppressNullableWarningExpression

namespace DimensionsHelper.Services.Metadata;

[DebuggerDisplay("Name = {Name}, FullName = {FullName}")]
public class Element : MetadataLabeledObject, IElement
{
    internal Element(IMetadataDocument document, string name)
        : base(document, ObjectType.Element, name)
    {
        ReferenceName = null;

        ElementType = ElementType.Category;
        Expression = null;
        Factor = null;
        Flag = CategoryFlags.None;
        IsSystem = false;
        IsDerived = false;
        HasNoData = false;
        Namespace = false;
        KeyCode = null;
        NativeValue = null;
        Nv = 0;
        Elements = new Elements(Document, this);
        LocalMultiplierVariables = new Variables(Document, this);
        LocalOtherVariables = new Variables(Document, this);

        DataSourceProperties = new Properties(Document, this);
    }

    public string? ReferenceName { get; set; }

    public string? Expression { get; set; }

    public object? Factor { get; set; }

    public string? OtherVariableId { get; set; }

    public string? MultiplierVariableId { get; set; }

    public string? CategoriesReference { get; internal set; }

    public string? KeyCode { get; set; }

    public ElementType ElementType { get; internal set; }

    public CategoryFlags Flag { get; set; }

    public bool IsSystem { get; internal set; }

    public IElementList? Reference
    {
        get
        {
            if (!string.IsNullOrEmpty(CategoriesReference) &&
                Document.Types.TryGetByKey(CategoriesReference!, out var list))
            {
                return list;
            }

            return null;
        }
    }


    public bool IsOtherLocal =>
        Flag.HasFlag(CategoryFlags.Other) && string.IsNullOrEmpty(OtherVariableId);

    public IVariable? OtherVariable
    {
        get
        {
            if (!IsOtherLocal && !string.IsNullOrEmpty(OtherVariableId))
            {
                return Document.Variables.TryGetByKey(OtherVariableId!, out var variable) ? variable : null;
            }

            return null;
        }
    }

    public IVariables LocalOtherVariables { get; }

    public bool IsMultiplierLocal =>
        Flag.HasFlag(CategoryFlags.Multiplier) && string.IsNullOrEmpty(MultiplierVariableId);

    public IVariable? MultiplierVariable
    {
        get
        {
            if (!IsMultiplierLocal && !string.IsNullOrEmpty(MultiplierVariableId))
            {
                return Document.Variables.TryGetByKey(MultiplierVariableId!, out var variable)
                    ? variable
                    : null;
            }

            return null;
        }
    }

    public IVariables LocalMultiplierVariables { get; }

    [MemberNotNullWhen(true, nameof(CategoriesReference))]
    public bool IsCategoriesReference => !string.IsNullOrEmpty(CategoriesReference);


    public IElementList? Categories =>
        string.IsNullOrEmpty(CategoriesReference) ? null : Document.Types.GetByKey(CategoriesReference!);

    public bool CanFilter => !Flag.HasFlag(CategoryFlags.NoFilter | CategoryFlags.Missing);

    public bool IsDerived { get; set; }

    public IProperties DataSourceProperties { get; }

    public bool Fixed => Flag.HasFlag(CategoryFlags.FixedPosition | CategoryFlags.Missing);

    public bool HasNoData { get; set; }

    public bool Exclusive => Flag.HasFlag(CategoryFlags.Exclusive | CategoryFlags.Missing);

    public bool Inline
    {
        get => Flag.HasFlag(CategoryFlags.Inline);

        set
        {
            if (value)
            {
                Flag |= CategoryFlags.Inline;
            }
            else
            {
                Flag &= ~CategoryFlags.Inline;
            }
        }
    }

    public bool Namespace { get; set; }

    public object? NativeValue { get; set; }

    public int Nv { get; set; }

    public int Value =>
        Document.CategoryMap.GetIdByName(FullName) ??
        throw new Exception($"未找到名称是'{FullName}'的分类值。");

    public IElements Elements { get; }

    protected override string GetFullName()
    {
        var name = Name;
        var parent = Parent;

        while (parent != null)
        {
            if (parent is IElement { Namespace: true } element)
            {
                name = element.Name + "." + name;
            }
            else if (parent is IElementList { Namespace: true } elementList)
            {
                name = elementList.Name + "." + name;
            }
            else if (parent is IField)
            {
                break;
            }

            parent = parent.Parent;
        }

        return name;
    }


    // 不允许修改全名
    protected override void SetFullName(string value)
    {
    }
}


[DebuggerDisplay("Count = {Count}")]
public class Elements : MetadataLabeledCollection<IElement>, IElements
{
    private bool _callAddNew;

    internal Elements(IMetadataDocument document) : base(document, ObjectType.Elements)
    {
        Namespace = false;
        Fixed = false;
        Inline = false;
        IsDbElements = false;
        IsSystem = false;
        IsDerived = false;
        ReferenceId = null;
    }

    internal Elements(IMetadataDocument document, IMetadataObject parent) : this(document)
    {
        Parent = parent;
    }


    public bool IsDerived { get; set; }

    public bool Namespace { get; set; }

    public bool Fixed { get; set; }

    public bool Inline { get; set; }

    public bool IsDbElements { get; internal set; }

    public string? ReferenceId { get; set; }

    public string? ReferenceName { get; set; }

    public IField? Owner { get; internal set; }

    public string? DbFilter { get; set; }

    public IElementList? Reference
    {
        get
        {
            if (string.IsNullOrEmpty(ReferenceId) ||
                !Document.Types.TryGetByKey(ReferenceId!, out var elements))
            {
                return null;
            }

            return elements;
        }
    }


#if NET8_0_OR_GREATER
    [MemberNotNullWhen(true, nameof(Reference))]
#endif
    public bool IsReference => Reference != null;



    public bool IsSystem { get; internal set; }

    public OrderTypes Order { get; set; }

    public override IElement CreateObject(string name)
    {
        return new Element(Document, name);
    }


    public IElement AddNew(string name)
    {
        var element = new Element(Document, name);
        _callAddNew = true;
        Add(element);
        Document.CategoryMap.Allocate(element.FullName);
        _callAddNew = false;
        return element;
    }

    public IElement AddNew(string name, string referenceId, string? referenceName = null)
    {
        var element = new Element(Document, name);
        _callAddNew = true;
        Add(element);
        element.CategoriesReference = referenceId;
        element.ReferenceName = referenceName;
        element.Inline = true;
        _callAddNew = false;

        // 将列表的分类定义加入 Owner.Categories 集合中。
        var refList = element.Reference ??
                      throw new Exception($"未找到UUID是'{referenceId}'的分类列表。");
        foreach (var cat in refList.Elements)
        {
            if (cat.ElementType != ElementType.Category)
            {
                continue;
            }
            ((Categories?)Owner?.Categories)?.Add(cat);
        }

        return element;
    }


    public IElement? Find(string name)
    {
        var node = DummyHead.Next;

        while (node != DummyHead)
        {
            var element = node.Data;
            var referenceList = element.Reference;
            if (referenceList != null)
            {
                var elementInList = referenceList.Elements.Find(name);
                if (elementInList != null)
                {
                    return elementInList;
                }
            }
            else if (name.Equals(element.Name, StringComparison.OrdinalIgnoreCase))
            {
                return element;
            }

            node = node.Next;
        }

        return null;
    }


    protected override string KeySelector(IElement item)
    {
        // Element 不允许重名
        return item.Name;
    }


    protected override void OnItemAdded(IElement item)
    {
        ((Element)item).Parent = this;

        // 对于新添加分类元素的情况，只有通过调用 AddNew 方法时，才触发 ItemChanged 事件。
        if (!_callAddNew)
        {
            return;
        }

        base.OnItemAdded(item);
    }
}


public class Categories : MetadataLabeledCollection<IElement>, ICategories
{
    internal Categories(IMetadataDocument document, IMetadataObject parent)
        : base(document, ObjectType.Categories, parent)
    {
    }

    public override IElement CreateObject(string name)
    {
        return new Element(Document, name);
    }

    protected override string KeySelector(IElement item)
    {
        return item.Id;
    }

    // 枚举元素时，如果存在引用列表，依次进入子列表

    private struct CategoriesEnumerator(Categories list) : IEnumerator<IElement>
    {
        private readonly int _version = list.Version;
        private readonly Node _dummyHead = list.DummyHead;
        private Node _current = list.DummyHead;
        private readonly Stack<IEnumerator<IElement>> _refStack = new();

        public readonly IElement Current =>
            _refStack.TryPeek(out var top) ? top.Current! : _current.Data;

        readonly object IEnumerator.Current => Current;

        public readonly void Dispose()
        {
        }

        private readonly void CheckVersion()
        {
            if (_version != list.Version)
            {
                throw new InvalidOperationException();
            }
        }

        public bool MoveNext()
        {
            while (true)
            {
                while (_refStack.TryPeek(out var top))
                {
                    if (top.MoveNext())
                    {
                        if (top.Current!.Reference == null)
                        {
                            return true;
                        }

                        // 如果引用列表，继续循环
                        _refStack.Push(top.Current.Reference.Elements.GetEnumerator());
                        continue;
                    }

                    _refStack.Pop().Dispose();
                }

                CheckVersion();

                if (_current.Next == _dummyHead)
                {
                    return false;
                }

                _current = _current.Next;

                if (_current.Data.Reference == null)
                {
                    return true;
                }

                // 如果引用了列表，添加到栈内，而后递归调用
                _refStack.Push(_current.Data.Reference.Elements.GetEnumerator());
            }
        }

        public void Reset()
        {
            CheckVersion();
            _current = _dummyHead;
        }


    }


    protected override IEnumerator<IElement> GetEnumeratorOverride()
    {
        return new CategoriesEnumerator(this);
    }


}


public class DbElements : Elements, IDbElements
{
    internal DbElements(IMetadataDocument document, IMetadataObject parent)
        : base(document, parent)
    {
        ObjectType = ObjectType.DbElements;
        DbQuestionOptions = new DbQuestionOptions(this);
    }

    public DbQuestionOptions DbQuestionOptions { get; }
}


public class ElementInstance : MetadataObject, IElementInstance
{
    internal ElementInstance(IMetadataDocument document, string fullName) : base(document,
        ObjectType.ElementInstance)
    {
        FullName = fullName;
    }

    public string FullName { get; set; }
}


public class ElementInstances : MetadataObjectCollection<ElementInstance>, IElementInstances
{

    internal ElementInstances(IMetadataDocument document, IMetadataObject parent)
    {
        Document = document;
        Parent = parent;
        ObjectType = ObjectType.ElementInstances;
        Versions = null;
        Versioned = true;
    }

    public IMetadataDocument Document { get; }

    public ObjectType ObjectType { get; }

    public bool Versioned { get; set; }


    public IMetadataObject Parent { get; }

    public IVersionObjectCollection? Versions { get; internal set; }

    public T? FindParent<T>() where T : IMetadataObject
    {
        return MetadataObject.FindParent<T>(this);
    }

}